﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TARLib
{
    public class Banconota
    {
        public Banconota(Decimal valore, int ordine)
        {
            Valore = valore;
            Ordine = ordine;
        }

        public Decimal Valore { get; set; }
        public int Ordine { get; set; }
    }

    public class RisultatoCalcoloResto
    {
        public RisultatoCalcoloResto(Decimal contanti, Decimal resto, int nMinBanconoteContanti, int nMinBanconoteResto)
        {
            Contanti = contanti;
            Resto = resto;
            NMinBanconoteContanti = nMinBanconoteContanti;
            NMinBanconoteResto = nMinBanconoteResto;
        }

        public Decimal Contanti { get; set; }
        public int NMinBanconoteContanti { get; set; }
        public Decimal Resto { get; set; }
        public int NMinBanconoteResto { get; set; }
    }

    public static class Calcolo
    {
        // Calcola i contanti sensati per pagare un totale
        public static List<RisultatoCalcoloResto> CalcolaRestiSuggeriti(Decimal totale)
        {
            List<RisultatoCalcoloResto> retList = new List<RisultatoCalcoloResto>();

            // Esempio: 170,00 €
            // Ordini di grandezza: 10^1 e 10^2 --> Minimo: 10^1
            // Usa solo: 10, 20, 50, 100, 200, 500
            List<Banconota> BanconoteDaUsare = new List<Banconota>();

            int totaleIntero = (int)(totale * 100);
            if ((totaleIntero % 10) != 0)
            {
                BanconoteDaUsare = GetAllBanconote().Where(o => o.Ordine >= -2).ToList();
            }
            else if ((totaleIntero % 100) != 0)
            {
                BanconoteDaUsare = GetAllBanconote().Where(o => o.Ordine >= -1).ToList();
            }
            else if ((totaleIntero % 1000) != 0)
            {
                BanconoteDaUsare = GetAllBanconote().Where(o => o.Ordine >= 0).ToList();
            }
            else if ((totaleIntero % 10000) != 0)
            {
                BanconoteDaUsare = GetAllBanconote().Where(o => o.Ordine >= 1).ToList();
            }
            else
            {
                BanconoteDaUsare = GetAllBanconote().Where(o => o.Ordine >= 2).ToList();
            }

            BanconoteDaUsare.Add(new Banconota(0, 0));
            // A questo punto con le BanconoteDaUsare trova i contanti "sensati"
            List<Decimal> valoriDaTestare = new List<decimal>();
            foreach (Banconota b1 in BanconoteDaUsare)
            {
                foreach (Banconota b2 in BanconoteDaUsare)
                {
                    foreach (Banconota b3 in BanconoteDaUsare)
                    {
                        valoriDaTestare.Add(b1.Valore + b2.Valore + b3.Valore);
                    }
                }
                

                //tempCount = valoriDaTestare.Count;

                //for (int i = 0; i < 10; i++)
                //{
                //    valoriDaTestare.Add(totale + b.Valore*i);
                //}

                ////for (int i = 0; i < 10; i++)
                ////{
                //if (tempCount > 0)
                //{
                //    for (int j = 0; j < tempCount; j++)
                //    {
                //        if (!valoriDaTestare.Contains(valoriDaTestare[j] + b.Valore))
                //            valoriDaTestare.Add(valoriDaTestare[j] + b.Valore);
                //    }
                //}
                //else
                //{
                //    if (!valoriDaTestare.Contains(totale + b.Valore))
                //        valoriDaTestare.Add(totale + b.Valore);
                //}
                ////}
            }

            valoriDaTestare.Add(totale);
            foreach (Decimal d in valoriDaTestare.Where(o => o >= totale).Distinct())
            {
                // Console.Write("\nContanti: " + d.ToString() + " | Resto: " + (d - totale).ToString());

                List<Banconota> BanconoteContanti = GetMinBanconote(BanconoteDaUsare, d);
                List<Banconota> BanconoteResto = GetMinBanconote(BanconoteDaUsare, d - totale);

                if (!BanconoteContanti.Select(o => o.Valore).Any(o => BanconoteResto.Select(p => p.Valore).Contains(o)))
                {
                    retList.Add(new RisultatoCalcoloResto(d, d - totale, BanconoteContanti.Count, BanconoteResto.Count));
                }
            }

            return retList.OrderBy(o => o.Contanti).ToList();
        }

        private static List<Banconota> GetMinBanconote(List<Banconota> banconoteDaUsare, Decimal importo)
        {
            List<Banconota> retList = new List<Banconota>();
            Decimal valoreMin = banconoteDaUsare.Min(o => o.Valore);

            while (importo > valoreMin)
            {
                Banconota banconotaToAdd = banconoteDaUsare.Where(o => o.Valore <= importo).OrderBy(o => o.Valore).Last();
                retList.Add(new Banconota(banconotaToAdd.Valore, banconotaToAdd.Ordine));
                importo = importo - banconotaToAdd.Valore;
            }

            return retList;
        }

        private static List<Banconota> GetAllBanconote()
        {
            List<Banconota> retList = new List<Banconota>();

            retList.Add(new Banconota(new Decimal(0.01), -2));
            retList.Add(new Banconota(new Decimal(0.02), -2));
            retList.Add(new Banconota(new Decimal(0.05), -2));
            retList.Add(new Banconota(new Decimal(0.1), -1));
            retList.Add(new Banconota(new Decimal(0.2), -1));
            retList.Add(new Banconota(new Decimal(0.5), -1));
            retList.Add(new Banconota(new Decimal(1), 0));
            retList.Add(new Banconota(new Decimal(2), 0));
            retList.Add(new Banconota(new Decimal(5), 0));
            retList.Add(new Banconota(new Decimal(10), 1));
            retList.Add(new Banconota(new Decimal(20), 1));
            retList.Add(new Banconota(new Decimal(50), 1));
            retList.Add(new Banconota(new Decimal(100), 2));
            //retList.Add(new Banconota(new Decimal(200), 2));
            //retList.Add(new Banconota(new Decimal(500), 2));

            return retList;
        }
    }
}